package one.stand.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.ObjectId;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.system.SystemUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;
import com.alipay.easysdk.kernel.util.ResponseChecker;
import com.alipay.easysdk.payment.app.models.AlipayTradeAppPayResponse;
import com.alipay.easysdk.payment.common.models.AlipayTradeRefundResponse;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.jmp.base.dto.AccountCompanyKeyRequest;
import com.jmp.base.dto.AccountResponse;
import com.jmp.base.dto.OrderPayWxAppVo;
import com.jmp.feign.AccountFeign;
import com.jmp.service.crm.dto.OrderPayBackDto;
import com.jmp.service.crm.dto.OrderPayDto;
import com.jmp.service.crm.dto.OrderRefundDto;
import lombok.extern.slf4j.Slf4j;
import one.stand.advice.ResultEnum;
import one.stand.config.WxUrlConfig;
import one.stand.dto.ConvertUtils;
import one.stand.enums.LoginEnum;
import one.stand.enums.OrderStateEnum;
import one.stand.enums.RefundStateEnum;
import one.stand.mapper.AddressMapperExt;
import one.stand.mapper.OrdersMapperExt;
import one.stand.mapper.PaymentRecordMapperExt;
import one.stand.model.Address;
import one.stand.model.Orders;
import one.stand.model.PaymentRecord;
import one.stand.model.ResultModel;
import one.stand.service.OrdersPayService;
import one.stand.service.OrdersService;
import one.stand.service.user.UserAdminServiceImpl;
import one.stand.util.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.*;

@Service
@Slf4j
public class OrdersPayServiceImpl implements OrdersPayService {
    private final static String body = "商品购买";

    /**
     * 成功码
     */
    public final static String SUCCESS = "SUCCESS";

    /**
     * 自定义参数，可以为终端设备号(门店号或收银设备ID)，PC网页或公众号内支付可以传"WEB"
     */
    private final static String deviceInfo = "";

    /**
     * 符合ISO 4217标准的三位字母代码，默认人民币：CNY
     */
    private final static String fee_type = "CNY";

    /**
     * JSAPI：JSAPI支付 NATIVE：Native支付 APP：APP支付
     */
    private final static String trade_type = "JSAPI";
    /**
     * JSAPI：JSAPI支付 NATIVE：Native支付 APP：APP支付
     */
    private final static String TRADE_TYPE_APP = "APP";

    @Autowired
    private AccountFeign accountFeign;

    @Autowired
    private WxUrlConfig wxUrlConfig;
    @Autowired
    private UserAdminServiceImpl userAdminService;

    @Autowired
    private AddressMapperExt addressMapperExt;
    @Autowired
    private OrdersMapperExt ordersMapperExt;
    @Autowired
    private OrdersService ordersService;
    @Autowired
    private PaymentRecordMapperExt paymentRecordMapperExt;

    @PostConstruct
    public void init() {
        // 1. 设置参数（全局只需设置一次）
        Factory.setOptions(getOptions());
    }

    private Config getOptions() {
        Config config = new Config();
        config.protocol = "https";
        config.gatewayHost = "openapi.alipay.com";
        config.signType = "RSA2";

        config.appId = "2021001167664914";

        // 为避免私钥随源码泄露，推荐从文件中读取私钥字符串而不是写入源码中
        config.merchantPrivateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCyNykpNCJMBnZV7NUYPnzn5cQmB9v9mLp5m9oZhB9Cx/bNUoRD44imE5oyIyWIsP1N+YCj58ln0OF+UIR/ao0ezBqq4Q6CTInvPPTFHHtVQCRDwM6LD0LYS2bxZAMNfMqasPdq+2+AvRcwuLkYDpCIhuNVxANIN73xfq6MBipUVEl1CnCxM6rnpnsG76mfFmhOep+1EWJf6X64zd89yfoh00ZDqHuqjbYuvWEqiVW6V2KgYQbGdATQZwh5cOnHny5jv9IGETgNRZXMMr61VvYqIZMNcMHsh0K1N84CJ0U56u4JgBMgNvxetcH/pYpGf1CKHmE8dUCK74dvpsKI2jtzAgMBAAECggEATK6NaGxFQZ8aNdikzDy+8+0aXB7PmqunuQHFc2s1JD/0Qt55iNiTiJowo9gZo1o8c9uibWQjdicbj4VHraeENzctVk9mNcyHIUiCLXoW21SxBfIlLRKQ+Y5gKgeVhK0oZC03GHI9xRAXtWAWTHKf4A7iLJodq2pIsWuH4ToZ5pRwsERWzQ/pEG8BfAudRNOeRSDx8yESRpcUGd9Nj+SOpS4TVL5xACJ8hDiHw3LmYe01vq30t871bBigf8dPFcT+hEKgsVjlIWUWkrz7LV7uGNHQqPJVxLG3a0E2FvpQu5GlMb7FYOrvZdwFrB+TPzGa1bquvZpr4fKIWjdafnuwAQKBgQDwzbBF2m/Ra70XMzvzcQ6WPYqeisBoZnZcUyRv478ytlu2fmLR5cVWT/7D4LWroeIVsPXT2oZ6hifqOj1YnulmzRS9eWdAjvILlrOtuCqj9CqgTcTVqUClgj/96kWhddJZ+GAqZbXBkooYZ7oykKI/XEwgPA1p1AddUAHa5X1DcwKBgQC9dlQ2GfvQ+8wtNhLuc9EWHqKAiJ8zaDPFN698fH5JbtsVL/1yg0A4hKaX4L0ydCcxia44fR7jT8sANknKwqaUdQEnA9kbMig35bOqwS64/3yBHgp58rgn4UjVeZsj1cua9n+KBrJJQIq8LOt3YwZrLbd8mSf+1l5e70AOZSEoAQKBgFOut15fHdLCRTElzeS42l/dvSmBgIIHcYR+JEB5/fzqoI6X91GILv7ix99cXOXrWP0+xP89htRXgNGGMcF1YBkoldeOtBIcWwMp+QEa6j6V4KZawFeb/OpHjGN6IX6Nym8XwJubQwGlIjZ+/ZcK6kdolM/y7ppjY7fK0Qo4DEL/AoGBALvPvu7alxu1X9M5lU63lH7+40vTIy3Mj/VinB0lM/MLZzX7mpyXq2Pba2GHMUYAipG6z+/DaIkhIjUVm9TZXFQyIHBSijdBY0j19ULBudU9vmgoJW55w0AVPSQRJk9BslHtJMTomneu+VhMlyaEDWrVqNxk3pbqXiZxuOgMK/ABAoGAQ35kEL9WjinqrohX+V6wQ0wBal2dZhsOA+DfGDqivbo44k6p4doq5gexOEdUKspWmU9c4UZn+v31xfI5rsxSxKUU8yKMy/T6+p7BDyDC4NrdAV9XGUqiZ/sdPWsDTbWBWVQ940HnUEf0uyh8piKnRD6YBSOOkd17jyc5PHlSJmw=";

        //注：证书文件路径支持设置为文件系统中的路径或CLASS_PATH中的路径，优先从文件系统中加载，加载失败后会继续尝试从CLASS_PATH中加载
//        config.merchantCertPath = "<-- 请填写您的应用公钥证书文件路径，例如：/foo/appCertPublicKey_2019051064521003.crt -->";
//        config.alipayCertPath = "<-- 请填写您的支付宝公钥证书文件路径，例如：/foo/alipayCertPublicKey_RSA2.crt -->";
//        config.alipayRootCertPath = "<-- 请填写您的支付宝根证书文件路径，例如：/foo/alipayRootCert.crt -->";

        //注：如果采用非证书模式，则无需赋值上面的三个证书路径，改为赋值如下的支付宝公钥字符串即可
        config.alipayPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy2qC2WwI3xBTb8jZi/NA+JIJbx/FeP6h+IldhlOyw6A9/2KL5HIrKufAOPlKSLvFQ/HNHfHmvBq0tCNX+QPa7FZNROS42c6prQpXWdnPMwzTIEgpwBfzUD+Ep6zE9skDDYi62o2gxf8/2kxjIyxOHOzO9PwgckuT2OMEOGu0GJB6FLN49o5lAC3MVRzc5JGxWzP9WzsYkP7TMOX0ZehEYjEoJAcudPYF7v+pXkJmJqlv6Qb5glTU233o/RanCdFi/EXG/SFlSFRkLb7Tj49OiViVRPxnYiUd+DUNmyOmfjiPsFUC6QgRLQkkVM/CMoe4u8M+6dQGTIbK3R74//yI3wIDAQAB";

        //可设置异步通知接收服务地址（可选）
        config.notifyUrl = wxUrlConfig.getAppAlipayBackUrl();

        //可设置AES密钥，调用AES加解密相关接口时需要（可选）
//        config.encryptKey = "<-- 请填写您的AES密钥，例如：aa4BtZ4tspm2wnXLb1ThQA== -->";

        return config;
    }

    @Override
    public Map<String, String> payWxJsapi(Integer orderId, Integer addressId, String remark, Integer optUserId) {
        if (Objects.nonNull(addressId)) {
            Address address = addressMapperExt.selectByPrimaryKey(addressId);
            AssertUtil.isTrue(optUserId.equals(address.getUserId()), ResultEnum.PARAM_CHECK);

            // 添加收货地址
            Orders model = new Orders();
            model.setOrdersId(orderId);
            model.setAddressId(addressId);
            model.setAddressName(address.getReceiveName());
            model.setAddressPhone(address.getReceivePhone());
            model.setAddressDetail(StrUtil.concat(true, address.getLocationAddress(), address.getDetailedAddress()));
            model.setRemark(remark);
            ordersMapperExt.updateByPrimaryKeySelective(model);
        }

        //获取订单信息
        OrderPayDto orderPayDto = payPre(Lists.newArrayList(orderId), optUserId, TYPE_WX);
        // 调用微信支付
        Map<String, String> payInfo = wxPayJsApi(orderPayDto);
        AssertUtil.nonNull(payInfo, ResultEnum.FAIL);
        return payInfo;
    }

    @Override
    public void payWxJsapiBack(String notifyData) {
        log.info("微信回调参数：{}", notifyData);

        OrderPayBackDto backDto = null;
        try {
            Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyData);
            WxConfig wxConfig = wxConfig(null);
            WXPay wxpay = new WXPay(wxConfig);
            String result_code = notifyMap.get("result_code");//业务结果	result_code	是	String(16)	SUCCESS/FAIL
            if (wxpay.isPayResultNotifySignatureValid(notifyMap) && "SUCCESS".equals(result_code)) {
                String outTradeNo = notifyMap.get("out_trade_no");
                String totalFee = notifyMap.get("total_fee");

                backDto = OrderPayBackDto.builder()
                        .outTradeNo(outTradeNo)
                        .totalFee(Integer.parseInt(totalFee))
                        .build();
            } else {
                log.info("pay status:{}, no handle", result_code);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        payBack(backDto);
    }

    @Override
    public OrderPayWxAppVo payWxApp(List<Integer> orderIds, Integer optUserId) {
        //获取订单信息
        OrderPayDto orderPayDto = payPre(orderIds, optUserId, TYPE_WX_APP);

        //微信下单
        OrderPayWxAppVo payWxResponse = wxPayApp(orderPayDto);
        AssertUtil.nonNull(payWxResponse, ResultEnum.PARAM_CHECK, "支付失败");

        return payWxResponse;
    }

    @Override
    public void payWxAppBack(String notifyData) {
        log.info("微信回调参数：{}", notifyData);

        OrderPayBackDto backDto = null;
        try {
            Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyData);
            WxConfig wxConfig = wxConfigApp();
            WXPay wxpay = new WXPay(wxConfig);
            String result_code = notifyMap.get("result_code");//业务结果	result_code	是	String(16)	SUCCESS/FAIL
            if (wxpay.isPayResultNotifySignatureValid(notifyMap) && "SUCCESS".equals(result_code)) {
                String outTradeNo = notifyMap.get("out_trade_no");
                String totalFee = notifyMap.get("total_fee");

                backDto = OrderPayBackDto.builder()
                        .outTradeNo(outTradeNo)
                        .totalFee(Integer.parseInt(totalFee))
                        .build();
            } else {
                log.info("pay status:{}, no handle", result_code);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        payBack(backDto);
    }

    @Override
    public String payAlipay(List<Integer> orderIds, Integer optUserId) {
        try {
            //获取订单信息
            OrderPayDto orderPayDto = payPre(orderIds, optUserId, TYPE_ALIPAY);

            // 2. 发起API调用
            AlipayTradeAppPayResponse response = Factory.Payment.App()
                    .pay(body, orderPayDto.getOutTradeNo(), MoneyUtil.getMoney(orderPayDto.getTotalFee()).toString());
            // 3. 处理响应或异常
            if (ResponseChecker.success(response)) {
                log.info("调用成功");
                return response.body;
            } else {
                log.error("调用失败，原因：{}", JSON.toJSONString(response));
            }
        } catch (Exception e) {
            log.error("调用遭遇异常", e);
        }
        AssertUtil.fail(ResultEnum.PARAM_CHECK);
        return null;
    }

    @Override
    public void payAlipayBack(Map<String, String> parameters) {
        OrderPayBackDto backDto = null;
        try {
            boolean flag = Factory.Payment.Common().verifyNotify(parameters);
            String trade_status = parameters.get("trade_status");
            if (flag && ("TRADE_SUCCESS".equals(trade_status) || "TRADE_FINISHED".equals(trade_status))) {
                //只有交易通知状态为 TRADE_SUCCESS 或 TRADE_FINISHED 时，支付宝才会认定为买家付款成功
                //状态 TRADE_SUCCESS 的通知触发条件是商户签约的产品支持退款功能的前提下，买家付款成功；
                //交易状态 TRADE_FINISHED 的通知触发条件是商户签约的产品不支持退款功能的前提下，买家付款成功；或者，商户签约的产品支持退款功能的前提下，交易已经成功并且已经超过可退款期限。
                String out_trade_no = parameters.get("out_trade_no");
                BigDecimal totalFee = new BigDecimal(parameters.get("total_amount"));
                backDto = OrderPayBackDto.builder()
                        .outTradeNo(out_trade_no)
                        .totalFee(MoneyUtil.getMoneyInt(totalFee))
                        .build();
            } else {
                log.info("pay status:{}, no handle", trade_status);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }

        payBack(backDto);
    }

    @Override
    public void refund(Integer orderId) {
        Orders orders = ordersMapperExt.selectByPrimaryKey(orderId);
        AssertUtil.isTrue(orders.getCompanyId().equals(RequestContextHolder.getRequestContext().getUser().getCompanyId()), ResultEnum.FAIL);
        PaymentRecord paymentRecord = paymentRecordMapperExt.selectByNo(orders.getTradeNo());
        OrderRefundDto orderRefundDto = OrderRefundDto.builder()
                .orderId(orderId)
                .tradeNo(orders.getTradeNo())
                .totalFee(paymentRecord.getTotalFee())
                .refundRemark(orders.getRefundRemark())
                .appId(paymentRecord.getAppid())
                .build();

        boolean flag = false;
        if (paymentRecord.getType().equals(TYPE_ALIPAY)) {
            flag = refundAlipay(orderRefundDto);
        } else if (paymentRecord.getType().equals(TYPE_WX)) {
            flag = refundWxJsApi(orderRefundDto);
        } else if (paymentRecord.getType().equals(TYPE_WX_APP)) {
            flag = refundWxApp(orderRefundDto);
        }
        AssertUtil.isTrue(flag, ResultEnum.FAIL);

        ordersService.refundSuccess(orderRefundDto);
    }

    private boolean refundWxApp(OrderRefundDto orderRefundDto) {
        try {
            String refundNo = getRefundNo();
            WxConfig wxConfig = wxConfigApp();
            WXPay wxpay = new WXPay(wxConfig);
            Map<String, String> data = new HashMap<>(10);
            data.put("appid", wxConfig.getAppID());
            data.put("mch_id", wxConfig.getMchID());
            data.put("nonce_str", getRandomNumber());
            data.put("out_trade_no", orderRefundDto.getTradeNo());
            data.put("out_refund_no", refundNo);
            data.put("total_fee", String.valueOf(orderRefundDto.getTotalFee()));
            data.put("refund_fee", String.valueOf(orderRefundDto.getTotalFee()));
            data.put("refund_desc", StringUtils.defaultIfBlank(orderRefundDto.getRefundRemark(), "退款"));
            /** data.put("notify_url", wxConfig.getRefundUrl());*/
            log.info("微信退款：{}", JSON.toJSONString(data));
            String sign = WXPayUtil.generateSignature(data, wxConfig.getKey());
            data.put("sign", sign);
            Map<String, String> resp = wxpay.refund(data);
            if (SUCCESS.equals(resp.get("return_code"))) {
                if (SUCCESS.equals(resp.get("result_code"))) {
                    orderRefundDto.setRefundNo(refundNo);
                    return true;
                }
            }
            log.info("微信退款失败，返回：{}", JSON.toJSONString(resp));
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }

        return false;
    }

    private boolean refundWxJsApi(OrderRefundDto orderRefundDto) {
        try {
            String refundNo = getRefundNo();
            WxConfig wxConfig = wxConfig(orderRefundDto.getAppId());
            WXPay wxpay = new WXPay(wxConfig);
            Map<String, String> data = new HashMap<>(10);
            data.put("appid", wxConfig.getAppID());
            data.put("mch_id", wxConfig.getMchID());
            data.put("nonce_str", getRandomNumber());
            data.put("out_trade_no", orderRefundDto.getTradeNo());
            data.put("out_refund_no", refundNo);
            data.put("total_fee", String.valueOf(orderRefundDto.getTotalFee()));
            data.put("refund_fee", String.valueOf(orderRefundDto.getTotalFee()));
            data.put("refund_desc", orderRefundDto.getRefundRemark());
            /** data.put("notify_url", wxConfig.getRefundUrl());*/
            String sign = WXPayUtil.generateSignature(data, wxConfig.getKey());
            data.put("sign", sign);
            Map<String, String> resp = wxpay.refund(data);
            if (SUCCESS.equals(resp.get("return_code"))) {
                if (SUCCESS.equals(resp.get("result_code"))) {
                    orderRefundDto.setRefundNo(refundNo);
                    return true;
                }
            }
            log.info("微信退款失败：{}", JSON.toJSONString(resp));
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }

        return false;
    }

    private boolean refundAlipay(OrderRefundDto orderRefundDto) {

        try {
            AlipayTradeRefundResponse response = Factory.Payment.Common().refund(orderRefundDto.getTradeNo(), MoneyUtil.getMoney(orderRefundDto.getTotalFee()).toString());
            if (ResponseChecker.success(response)) {
                orderRefundDto.setRefundNo(getRefundNo());
                return true;
            } else {
                log.error("Alipay退款调用失败，原因：{}", JSON.toJSONString(response));
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return false;
    }

    private OrderPayDto payPre(List<Integer> orderIds, Integer optUserId, Integer type) {
        // 判断订单状态
        String orderIdsStr = StringUtils.EMPTY;
        Integer totalFee = 0;
        for (Integer id : orderIds) {
            Orders orders = ordersMapperExt.selectByPrimaryKey(id);
            if (orders == null || orders.getOrderPrice() <= 0
                    || !OrderStateEnum.WAIT_PAYMENT.getCode().equals(orders.getOrderState())
                    || !RefundStateEnum.NO_REFUND.getCode().equals(orders.getRefundState())) {
                continue;
            }
            orderIdsStr = "," + id;
            totalFee = totalFee + orders.getOrderPrice();
        }

        AssertUtil.nonNull(orderIdsStr, ResultEnum.PARAM_CHECK, "无支付订单");

        // 查询用户openId信息
        final RequestContext.User optUser = RequestContextHolder.getRequestContext().getUser();
        LoginEnum loginEnum = LoginEnum.getByCode(optUser.getLoginProgramName());
        String openId = userAdminService.getOpenId(optUser.getUserId(), loginEnum.getAccountType(), optUser.getCompanyKey());
        AccountCompanyKeyRequest accountCompanyKeyRequest = AccountCompanyKeyRequest.builder()
                .companyKey(optUser.getCompanyKey())
                .loginEnum(loginEnum)
                .build();
        ResultModel<AccountResponse> accountResponseResultModel = accountFeign.getCompanyKeyDefaultIfNull(accountCompanyKeyRequest);
        AssertUtil.isTrue(accountResponseResultModel);

        //insert record
        PaymentRecord paymentRecord = new PaymentRecord();
        paymentRecord.setType(type);
        paymentRecord.setUserId(optUserId);
        paymentRecord.setOutTradeNo(ObjectId.next());
        paymentRecord.setOrderIds(orderIdsStr.substring(1));
        paymentRecord.setTotalFee(totalFee);
        paymentRecord.setAppid(accountResponseResultModel.getData().getAppId());
        paymentRecord.setOpenid(openId);
        paymentRecord.setStatus(0);
        paymentRecord.setCreateTime(new Date());
        paymentRecordMapperExt.insertSelective(paymentRecord);

        return OrderPayDto.builder()
                .outTradeNo(paymentRecord.getOutTradeNo())
                .totalFee(paymentRecord.getTotalFee())
                .orderIds(paymentRecord.getOrderIds())

                .appId(paymentRecord.getAppid())
                .openId(paymentRecord.getOpenid())
                .build();
    }

    private void payBack(OrderPayBackDto backDto) {
        AssertUtil.nonNull(backDto, ResultEnum.PARAM_CHECK);

        try {
            PaymentRecord paymentRecord = paymentRecordMapperExt.selectByNo(backDto.getOutTradeNo());
            AssertUtil.nonNull(paymentRecord, ResultEnum.PARAM_CHECK);
            AssertUtil.isTrue(paymentRecord.getStatus() == 0, ResultEnum.PARAM_CHECK);
            AssertUtil.isTrue(backDto.getTotalFee().equals(paymentRecord.getTotalFee()), ResultEnum.PARAM_CHECK, "金额校验不一致");
            backDto.setPayId(paymentRecord.getPaymentRecordId());
            backDto.setOrderIds(ConvertUtils.list(StrUtil.split(paymentRecord.getOrderIds(), CharUtil.COMMA), Integer::valueOf));

            ordersService.payBack(backDto);

            payUpdate(backDto, true);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            if (Objects.nonNull(backDto)) {
                payUpdate(backDto, false);
            }
            throw e;
        }
    }

    private void payUpdate(OrderPayBackDto backDto, boolean isSuccess) {
        if (Objects.isNull(backDto.getPayId())) return;

        PaymentRecord paymentRecord = new PaymentRecord();
        paymentRecord.setPaymentRecordId(backDto.getPayId());
        if (isSuccess) {
            paymentRecord.setStatus(1);
            paymentRecord.setTimeEnd(DateUtil.now());
        } else {
            paymentRecord.setStatus(2);
        }
        paymentRecordMapperExt.updateByPrimaryKeySelective(paymentRecord);
    }

    private Map<String, String> wxPayJsApi(OrderPayDto orderPayDto) {
        log.info("微信支付请求参数：{}", JSONObject.toJSONString(orderPayDto));
        try {
            WxConfig wxConfig = wxConfig(orderPayDto.getAppId());
            WXPay wxpay = new WXPay(wxConfig);
            Map<String, String> data = new HashMap<>(12);
            data.put("appid", wxConfig.getAppID());
            data.put("mch_id", wxConfig.getMchID());
            data.put("body", body);
            data.put("out_trade_no", orderPayDto.getOutTradeNo());
            data.put("device_info", deviceInfo);
            data.put("fee_type", fee_type);
            data.put("total_fee", String.valueOf(orderPayDto.getTotalFee()));
            data.put("spbill_create_ip", SystemUtil.getHostInfo().getAddress());
            data.put("notify_url", wxConfig.getNotifyUrl());
            data.put("trade_type", trade_type);
            data.put("openid", orderPayDto.getOpenId());
            String sign = null;
            sign = WXPayUtil.generateSignature(data, wxConfig.getKey());
            data.put("sign", sign);
            Map<String, String> resp = wxpay.unifiedOrder(data);
            log.info("微信支付返回：{}", JSON.toJSONString(resp));

            String returnCode = resp.get("return_code");
            if (SUCCESS.equals(resp.get("return_code"))) {
                Map<String, String> reData = new HashMap<>(7);
                reData.put("appId", wxConfig.getAppID());
                reData.put("nonceStr", resp.get("nonce_str"));
                String newPackage = "prepay_id=" + resp.get("prepay_id");
                reData.put("package", newPackage);
                reData.put("signType", "MD5");
                reData.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
                String newSign = WXPayUtil.generateSignature(reData, wxConfig.getKey());
                resp.put("paySign", newSign);
                resp.put("timeStamp", reData.get("timeStamp"));

                return resp;
            } else {
                log.error("prepay return code error:{}", returnCode);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }

    private OrderPayWxAppVo wxPayApp(OrderPayDto request) {
        log.info("微信支付请求参数：{}", JSONObject.toJSONString(request));
        try {
            WxConfig wxConfig = wxConfigApp();
            WXPay wxpay = new WXPay(wxConfig);
            Map<String, String> data = new HashMap<>(12);
            data.put("appid", wxConfig.getAppID());
            data.put("mch_id", wxConfig.getMchID());
            data.put("body", body);
            data.put("out_trade_no", request.getOutTradeNo());
            data.put("device_info", deviceInfo);
            data.put("fee_type", fee_type);
            data.put("total_fee", String.valueOf(request.getTotalFee()));
            data.put("spbill_create_ip", SystemUtil.getHostInfo().getAddress());
            data.put("notify_url", wxConfig.getNotifyUrl());
            data.put("trade_type", TRADE_TYPE_APP);
//            data.put("openid", request.getOpenId());
            String sign = WXPayUtil.generateSignature(data, wxConfig.getKey());
            data.put("sign", sign);
            Map<String, String> resp = wxpay.unifiedOrder(data);
            log.info("微信支付返回：{}", JSON.toJSONString(resp));

            String returnCode = resp.get("return_code");
            if (SUCCESS.equals(returnCode)) {//下单成功，组织APP端 请求参数
                Map<String, String> reData = Maps.newHashMap();
                reData.put("appid", wxConfig.getAppID());
                reData.put("partnerid", wxConfig.getMchID());
                reData.put("prepayid", resp.get("prepay_id"));
                reData.put("package", "Sign=WXPay");
                reData.put("noncestr", resp.get("nonce_str"));
                String time = String.valueOf(System.currentTimeMillis() / 1000);
                reData.put("timestamp", time);
                String newSign = WXPayUtil.generateSignature(reData, wxConfig.getKey());

                return OrderPayWxAppVo.builder()
                        .appid(wxConfig.getAppID())
                        .partnerid(wxConfig.getMchID())
                        .prepayid(reData.get("prepayid"))
                        .packageStr(reData.get("package"))
                        .noncestr(reData.get("noncestr"))
                        .timestamp(time)
                        .sign(newSign)
                        .build();
            } else {
                log.error("prepay return code error:{}", returnCode);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }

    /**
     * 获取默认支付账号
     */
    // TODO: 2020/6/20 待优化;可改为成员变量
    private WxConfig wxConfig(String appId) {
        ResultModel<AccountResponse> resultModel = accountFeign.getCompanyKeyDefaultIfNull(AccountCompanyKeyRequest.builder()
                .loginEnum(LoginEnum.WX_PAY)
                .build());
        AssertUtil.isTrue(resultModel);
        AccountResponse companyAccountModel = resultModel.getData();
        AssertUtil.nonNull(companyAccountModel, ResultEnum.PARAM_CHECK, "未获取到公司账户~!");

        WxConfig wxConfig = new WxConfig();
        wxConfig.setAppId(appId);
        wxConfig.setKey(companyAccountModel.getKey());
        wxConfig.setMchId(companyAccountModel.getMerchantId());
        wxConfig.setCertData(Base64.getDecoder().decode(companyAccountModel.getCertData()));
        wxConfig.setNotifyUrl(wxUrlConfig.getNotifyUrl());
        wxConfig.setRefundUrl(wxUrlConfig.getRefundUrl());
        wxConfig.setHttpConnectTimeoutMs(5000);
        wxConfig.setHttpReadTimeoutMs(5000);
        return wxConfig;
    }

    /**
     * 获取默认支付账号
     */
    // TODO: 2020/6/20 待优化;可改为成员变量
    private WxConfig wxConfigApp() {
        ResultModel<AccountResponse> resultModel = accountFeign.getCompanyKeyDefaultIfNull(AccountCompanyKeyRequest.builder()
                .loginEnum(LoginEnum.WX_PAY)
                .build());
        AssertUtil.isTrue(resultModel);
        AccountResponse companyAccountModel = resultModel.getData();
        AssertUtil.nonNull(companyAccountModel, ResultEnum.PARAM_CHECK, "未获取到公司账户~!");

        WxConfig wxConfig = new WxConfig();
        wxConfig.setAppId("wx00361f2e18fd26d2");
        wxConfig.setKey(companyAccountModel.getKey());//key为商户平台设置的密钥key
        wxConfig.setMchId(companyAccountModel.getMerchantId());//商户号
        wxConfig.setCertData(Base64.getDecoder().decode(companyAccountModel.getCertData()));//商户证书
        wxConfig.setNotifyUrl(wxUrlConfig.getAppBackUrl());
        wxConfig.setRefundUrl(wxUrlConfig.getRefundUrl());
        wxConfig.setHttpConnectTimeoutMs(5000);
        wxConfig.setHttpReadTimeoutMs(5000);
        return wxConfig;
    }

    /**
     * 根据时间戳生产退款订单编号
     */
    private static String getRefundNo() {
        return "TK" + System.currentTimeMillis() + "" + (int) (1 + Math.random() * (9999));
    }

    /**
     * 随机数
     */
    private static String getRandomNumber() {
        return System.currentTimeMillis() + "" + (int) (1 + Math.random() * (9999));
    }
}
